package msg

import (
	"bytes"
	"encoding/binary"
	"gitee.com/Cookie_XiaoD/go-terminalserver/protocol/std808core"
	"gitee.com/Cookie_XiaoD/go-terminalserver/protocol/std808core/body"
	"gitee.com/Cookie_XiaoD/go-terminalserver/util"
	"math"
)

//标准808消息
type Std808Msg struct {
	//消息ID
	MessageID Std808TerminalMsgID
	//消息体属性
	BodyProp uint16
	//是否分包
	IsMultiPackage bool
	//是否经过RSA加密
	ByRSA bool
	//消息体长度
	MessageLength uint16
	//终端手机号（SIM卡号）
	TerminalSIMBytes [6]byte
	TerminalSIM      string
	//消息流水号
	SerialNumber uint16
	//消息包封装项
	PackageItem *Std808MsgPackageItem
	//消息体
	MessageBody []byte
	//是否通过校验
	PassCheck bool
}

//标准808原始消息的消息包封装项
type Std808MsgPackageItem struct {
	//总包数
	TotalBagCount uint16
	//包序号 从1开始
	PackageNo uint16
}

//Unpack 终端上行数据解包成标准808消息
func Unpack(msg []byte) (*Std808Msg, error) {
	resultMsg := &Std808Msg{}
	msgLen := len(msg)
	tmpMsg := msg[1 : msgLen-1]
	reader := bytes.NewReader(tmpMsg)
	var err error
	err = binary.Read(reader, binary.BigEndian, &resultMsg.MessageID)
	err = binary.Read(reader, binary.BigEndian, &resultMsg.BodyProp)
	//判断是否分包
	flag := uint16(math.Pow(2, 13))
	resultMsg.IsMultiPackage = resultMsg.BodyProp&flag == flag
	//判断是否经过RSA加密
	flag = uint16(math.Pow(2, 10))
	resultMsg.ByRSA = resultMsg.BodyProp&flag == flag
	//消息体长度
	resultMsg.MessageLength = resultMsg.BodyProp << 6 >> 6

	err = binary.Read(reader, binary.BigEndian, &resultMsg.TerminalSIMBytes)
	//手机号一共11位，去除开头的0
	resultMsg.TerminalSIM = util.BCD2Str(resultMsg.TerminalSIMBytes[0:])[1:]

	err = binary.Read(reader, binary.BigEndian, &resultMsg.SerialNumber)

	//消息体开始索引
	bodyStartIndex := 13
	//如果分包则具有消息包封装项
	if resultMsg.IsMultiPackage {
		bodyStartIndex = 16
	}
	resultMsg.MessageBody = msg[bodyStartIndex : bodyStartIndex+int(resultMsg.MessageLength)]

	checkCode := util.GetXOR(msg, 1, msgLen-3)
	resultMsg.PassCheck = checkCode == msg[msgLen-2]

	if err != nil {
		return nil, err
	}
	return resultMsg, nil

}

//基础消息
type BasicMessage struct {
	MessageID        uint16
	NeedSplitPackage bool
	NeedRSA          bool
	TerminalSIM      string
	SerialNumber     uint16
	PackageItem      *Std808MsgPackageItem
	MessageBody      []byte
}

//NewPlateformBasicMessage 构建平台基础消息
func NewPlateformBasicMessage(
	msgID Std808PlateformMsgID,
	needRSA bool,
	terminalSIM string,
	sn uint16,
	packItem *Std808MsgPackageItem, data body.To808BytesConverter) *BasicMessage {

	msgBodyBytes := body.Get808Bytes(data)

	return &BasicMessage{
		MessageID:        uint16(msgID),
		NeedSplitPackage: false, //平台下发的消息不存在分包
		NeedRSA:          needRSA,
		TerminalSIM:      terminalSIM,
		SerialNumber:     sn,
		PackageItem:      packItem,
		MessageBody:      msgBodyBytes,
	}
}

func Pack(msg *BasicMessage) ([]byte, error) {
	var err error

	//TODO:未来处理RSA加密
	bodyLen := len(msg.MessageBody)
	if msg.NeedRSA && bodyLen > 0 {
		msg.MessageBody = msg.MessageBody
	}

	//808消息头中，只有9位可以表示长度，因此如果长度超过1023，理论必须分包
	//但是由于808中并没有对分包做具体的规定，所以可能平台分包了，但是终端根本不处理的情况
	//因此长度超过一包允许的消息体最大长度，直接算作失败
	if bodyLen > 1023 {
		return nil, std808core.Error_BodyLength
	}

	writer := new(bytes.Buffer)
	//消息ID
	err = binary.Write(writer, binary.BigEndian, msg.MessageID)
	//消息体属性
	if bodyLen == 0 {
		//无消息体时，分包与加密均为0
		writer.WriteByte(0)
		writer.WriteByte(0)
	} else {
		var temp uint16 = 0
		//是否分包
		if msg.NeedSplitPackage {
			temp |= uint16(math.Pow(2, 13))
		}
		//是否RSA加密
		if msg.NeedRSA {
			temp |= uint16(math.Pow(2, 10))
		}
		err = binary.Write(writer, binary.BigEndian, temp)
	}

	//终端手机号
	_, err = writer.Write(util.Str2BCD(msg.TerminalSIM))
	//消息流水号
	err = binary.Write(writer, binary.BigEndian, msg.SerialNumber)
	//消息包封装项
	if msg.NeedSplitPackage {
		//总包数
		err = binary.Write(writer, binary.BigEndian, msg.PackageItem.TotalBagCount)
		//包序号
		err = binary.Write(writer, binary.BigEndian, msg.PackageItem.PackageNo)
	}
	//消息体
	if bodyLen > 0 {
		_, err = writer.Write(msg.MessageBody)
	}

	if err != nil {
		return nil, err
	}
	//对不包含头尾标志位的部分进行校验码计算
	originBytes := writer.Bytes()
	//校验码
	checkCode := util.GetXOR(originBytes, 0, len(originBytes)-1)
	//计算校验码之后追加
	originBytes = append(originBytes, checkCode)

	//对不包含头尾标志位的部分进行转义
	escapeBytes := util.EscapeBytes(originBytes, 0x7e, 0x7d, 0x02, 0x01)

	//在转义后的内容头尾加上协议标记位
	retByes := make([]byte, 0, len(escapeBytes)+2)
	retByes = append(retByes, FlagByte)
	retByes = append(retByes, escapeBytes...)
	retByes = append(retByes, FlagByte)

	return retByes, nil
}
